package com.fancy.cc.security.aspect;

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;

import java.util.HashMap;
import java.util.Map;

/**
 * @author: yongxin@mail.thefancyapp.com
 * @program: fancy
 * @description: 统一日志集成 打印json
 * @create: 2020-05-19 12:16
 **/
public class LoggerHandler {

    private static Logger logger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);

    /**
     * 堆栈logger
     */
    private static Logger stackLogger = LoggerFactory.getLogger("stack");

    /**
     * 案发现场信息
     *
     * @param pop 相对于打印日志的位置的pop层栈帧，正为向上追溯调用栈帧，负为向下追寻被调栈帧
     * @return
     */
    public static String getScene(int... pop) {
        int popLevel = (null == pop || pop.length <= 0) ? 0 : pop[0];
        StackTraceElement[] stack = (new Throwable()).getStackTrace();
        //堆栈至少3层
        if (null == stack || stack.length <= 2) {
            return "[Not found log scene，Track by your self] - ";
        }
        StackTraceElement target = stack[2];
        for (int i = 0; i < stack.length - 3; i++) {
            //如果目标栈帧已经超出堆栈，则返回现场信息
            if ((i + 2 + popLevel) > (stack.length - 1) || (i + 2 + popLevel) < 0) {
                target = stack[i + 2];
                break;
            }
            // 从运行堆栈中找到目标栈帧（调用日志打印的位置）
            if (null != stack[i] && stack[i].getClassName().equals(LoggerHandler.class.getName())) {
                target = stack[i + 2 + popLevel];
                break;
            }
        }
        return (null == target) ? "[Not found log scene，Track by your self] - " : target.getClassName() + "." + target.getMethodName() + "(" + target.getLineNumber() + ") - ";
    }

    /**
     * info日志
     *
     * @param message 带占位符的字符串
     * @param objects 占位符替换值
     */
    public static void info(String message, Object... objects) {
        info(TYPE.MESSAGE,
                MessageFormatter.arrayFormat(message, objects).getMessage(),
                getScene());
    }

    /**
     * debug日志
     *
     * @param message 带占位符的字符串
     * @param objects 占位符替换值
     */
    public static void debug(String message, Object... objects) {
        debug(TYPE.DEBUG,
                MessageFormatter.arrayFormat(message, objects).getMessage(),
                getScene());
    }

    /**
     * warm日志
     *
     * @param message 带占位符的字符串
     * @param objects 占位符替换值
     */
    public static void warn(String message, Object... objects) {
        warn(TYPE.WARN,
                MessageFormatter.arrayFormat(message, objects).getMessage(),
                getScene());
    }

    /**
     * 堆栈及信息打印（私有方法）
     *
     * @param e       堆栈
     * @param message 含有占位符的字符串
     * @param objects 填充占位符的一系列对象
     */
    private static void trace(Throwable e, String message, Object... objects) {
        stackLogger.trace(getScene() + message, objects, e);
    }

    /**
     * error日志
     *
     * @param message 带占位符的字符串
     * @param objects 占位符替换值
     */
    public static void error(String message, Object... objects) {
        logger.error(getScene() + message, objects);
    }


    /**
     * 仅含有堆栈信息的error日志
     *
     * @param e
     */
    public static void error(Throwable e) {
        trace(e, null);
    }

    /**
     * 带字符串及堆栈信息的error日志
     *
     * @param e       堆栈
     * @param message 带占位符的字符串
     * @param objects 占位符替换值
     */
    public static void error(Throwable e, String message, Object... objects) {
        error(TYPE.ERROR,
                MessageFormatter.arrayFormat(message, objects).getMessage(),
                getScene());
        trace(e, message, objects);
    }

    /**
     * error 日志
     *
     * @param type        类型
     * @param formatValue 对应参数的值
     */
    public static void error(TYPE type, Object... formatValue) {
        logger.error(format(
                type,
                formatValue
        ));
    }

    /**
     * info 日志
     *
     * @param type        类型
     * @param formatValue 对应参数的值
     */
    public static void info(TYPE type, Object... formatValue) {
        logger.info(format(
                type,
                formatValue
        ));
    }

    /**
     * debug日志
     *
     * @param type        类型
     * @param formatValue 对应参数的值
     */
    public static void debug(TYPE type, Object... formatValue) {
        logger.debug(format(
                type,
                formatValue
        ));
    }

    /**
     * 带堆栈信息的debug日志
     *
     * @param e       堆栈
     * @param message 带占位符的字符串
     * @param objects 占位符替换值
     */
    public static void debug(Throwable e, String message, Object... objects) {
        debug(getScene() + message, objects, e);
        trace(e, message, objects);
    }

    /**
     * warn日志
     *
     * @param type        类型
     * @param formatValue 对应参数的值
     */
    public static void warn(TYPE type, Object... formatValue) {
        logger.warn(format(
                type,
                formatValue
        ));
    }


    /**
     * 格式化日志
     *
     * @param type   类型
     * @param arrays 数据 type.getParas(),一一对应的数据
     * @return json数据
     */
    public static String format(TYPE type, Object... arrays) {
        //无数据
        if (arrays == null) {
            arrays = EMPTY;
        }

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("type", type.getValue());

        //定义位置
        int i = 0;
        Map<String, Object> map = new HashMap<>(arrays.length << 1);
        //遍历
        for (String key : type.paras) {

            Object value;//数据对象
            //判断数组越界
            if (arrays.length <= i) {
                break;
            } else {
                value = arrays[i++];
            }

            map.put(key, value);
        }

        jsonObject.put("info", map);
        //转化
        return jsonObject.toJSONString();
    }

    /**
     * 空数组常量
     */
    private static final Object[] EMPTY = new Object[0];

    /**
     * 定义类型
     */
    public enum TYPE {
        //基本信息
        MESSAGE("message", "message", "position"),
        DEBUG("debug", "message", "position"),
        WARN("warn", "message", "position"),
        ERROR("error", "message", "position"),

        REQUEST_START("Request_start", "domain", "ip", "uri", "header","params", "method", "RequestID_start"),

        REQUEST_END("Request_end", "domain", "ip", "uri", "params","method", "result", "cost", "RequestID_end"),
        REQUEST_ERROR("Request_error", "domain", "ip", "uri", "header","params","method", "result", "cost", "RequestID_end"),

        MONITOR_REQUEST("monitor_request_cost", "domain", "ip", "uri", "cost", "urlPara", "method"),
        MONITOR_ADMIN("monitor_admin", "domain", "uri", "userName"),

        WXXCX("wxxcx_log", new String[]{"openid", "category"}),

        /**
         * 记录返回错误信息问题
         */
        RETURN_ERROR_MSG("return_error_msg", "key", "request_ip", "other_msg"),
        ;

        /**
         * @param value 类型名称
         * @param paras 参数名称
         */
        TYPE(String value, String... paras) {
            this.value = value;
            this.paras = paras;
        }

        /**
         * 类型-值
         */
        final String value;

        /**
         * 参数
         */
        final String[] paras;

        public String getValue() {
            return value;
        }

        public String[] getParas() {
            return paras;
        }
    }

}
